package junior.util.lambda;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;

import com.esotericsoftware.reflectasm.MethodAccess;

/**
 * 模拟函数式编程的简单类
 * 
 * @param <ReturnType>
 * @author ZhangXiaoye
 * @date 2016年6月17日 下午3:44:02
 */
public final class Function {
	
	// 无实例，全静态方法
	private Function(){ }
	
	/**
	 * 根据Key和Value生成对应的Entry
	 * 
	 * @param key
	 * @param value
	 * @return
	 * @author ZhangXiaoye
	 * @date 2016年9月23日 上午11:59:25
	 */
	public static <K, V> Entry<K, V> entry(K key, V value){
		return new AbstractMap.SimpleEntry<K, V>(key, value);
	}
	
	/**
	 * 将Entry的列表转换为Map，<br/>
	 * 转换后为<code>Map&lt;K, List&lt;V&gt;&gt;</code>，可以使用fold或reduce整理为<code>Map&lt;K, V&gt;</code>
	 * 
	 * @param collection
	 * @return
	 * @author ZhangXiaoye
	 * @date 2016年9月23日 下午12:52:19
	 */
	public static <K, V> Map<K, List<V>> entries(Collection<? extends Entry<? extends K, ? extends V>> collection){
		final Map<K, List<V>> ret = new LinkedHashMap<K, List<V>>();
		for(final Entry<? extends K, ? extends V> entry: collection){
			if(entry != null && entry.getKey() != null && entry.getValue() != null){
				List<V> list = ret.get(entry.getKey());
				if(list == null){
					list = new ArrayList<V>();
					ret.put(entry.getKey(), list);
				}
				list.add(entry.getValue());
			}
		}
		return ret;
	}
	
	/**
	 * 列表生成模式
	 * 
	 * @param n 从0,1,2....,(n - 1)
	 * @param function 生成函数（Integer i）
	 * @return 生成的列表
	 * @author ZhangXiaoye
	 * @date 2016年6月19日 下午8:51:07
	 */
	public static <O, T extends Exception> List<O> generate(final int n, final FuncIOE<Integer, O, T> function) throws T{
		final List<O> ret = new ArrayList<O>();
		for(int i = 0; i < n; i ++){
			ret.add(function.call(i));
		}
		return ret;
	}
	
	/**
	 * 类型映射转换（List转换为List）
	 * 
	 * @param collection 待转换的List&lt;I&gt;
	 * @param function 映射函数 (I) -> List&lt;O&gt;
	 * @return 转换后的List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年10月8日 上午10:41:04
	 */
	public static <I, O, T extends Exception> List<O> flatMap(final Collection<? extends I> collection, final FuncIOE<I, ? extends Collection<O>, T> function) throws T{
		if(collection == null){
			return Collections.emptyList();
		}
		final List<O> ret = new ArrayList<O>();
		for(final I obj: collection){
			final Collection<O> flats = function.call(obj);
			if(flats != null){
				ret.addAll(flats);
			}
		}
		return ret;
	}
	
	/**
	 * 类型映射转换（Map转换为List）
	 * 
	 * @param map 待转换的Map&lt;K, V&gt;
	 * @param function 映射函数 (Entry&lt;K, V&gt;) -> List&lt;O&gt;
	 * @return 转换后的List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午4:41:01
	 */
	public static <K, V, O, T extends Exception> List<O> flatMap(final Map<? extends K, ? extends V> map, final FuncIOE<Entry<K, V>, ? extends Collection<O>, T> function) throws T{
		if(map == null){
			return Collections.emptyList();
		}
		@SuppressWarnings("unchecked")
		final Map<K, V> converted = (Map<K, V>) map;
		return flatMap(converted.entrySet(), function);
	}
	
	/**
	 * 类型映射转换（List转换为List）
	 * 
	 * @param collection 待转换的List&lt;I&gt;
	 * @param function 映射函数 (I) -> O
	 * @return 转换后的List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年6月17日 下午3:54:27
	 */
	public static <I, O, T extends Exception> List<O> map(final Collection<? extends I> collection, final FuncIOE<I, O, T> function) throws T{
		if(collection == null){
			return Collections.emptyList();
		}
		final List<O> ret = new ArrayList<O>();
		for(final I obj: collection){
			ret.add(function.call(obj));
		}
		return ret;
	}
	
	/**
	 * 类型映射转换（Map转换为List）
	 * 
	 * @param map 待转换的Map&lt;K, V&gt;
	 * @param function 映射函数 (Entry&lt;K, V&gt;) -> O
	 * @return 转换后的List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午4:41:01
	 */
	public static <K, V, O, T extends Exception> List<O> map(final Map<? extends K, ? extends V> map, final FuncIOE<Entry<K, V>, O, T> function) throws T{
		if(map == null){
			return Collections.emptyList();
		}
		@SuppressWarnings("unchecked")
		final Map<K, V> converted = (Map<K, V>) map;
		return map(converted.entrySet(), function);
	}
	
	/**
	 * 类型映射转换（List转换为Map）
	 * 
	 * @param collection 待转换的List&lt;V&gt;
	 * @param function 映射函数 (V) -> Entry&lt;K, V&gt;
	 * @return 转换后的Map&lt;K, List&lt;V&gt;&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午7:08:37
	 */
	public static <I, K, V, T extends Exception> Map<K, List<V>> map(final Collection<? extends I> collection, final FuncIKVE<I, K, V, T> function) throws T{
		if(collection == null){
			return Collections.emptyMap();
		}
		final Map<K, List<V>> ret = new LinkedHashMap<K, List<V>>();
		for(final I obj: collection){
			final Entry<K, V> entry = function.call(obj);
			if(entry != null && entry.getKey() != null && entry.getValue() != null){
				List<V> list = ret.get(entry.getKey());
				if(list == null){
					list = new ArrayList<V>();
					ret.put(entry.getKey(), list);
				}
				list.add(entry.getValue());
			}
		}
		return ret;
	}
	
	/**
	 * 类型映射转换（Map转换为Map）
	 * 
	 * @param map 待转换的Map&ltKIn, VIn&gt;
	 * @param function 映射函数 (Entry&lt;KIn, VIn&gt;) -> Entry&lt;KOut, VOut&gt;
	 * @return 转换后的Map&lt;KOut, List&lt;VOut&gt;&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午4:45:59
	 */
	public static <KIn, VIn, KOut, VOut, T extends Exception> Map<KOut, List<VOut>> map(final Map<? extends KIn, ? extends VIn> map, final FuncIKVE<Entry<KIn, VIn>, KOut, VOut, T> function) throws T{
		if(map == null){
			return Collections.emptyMap();
		}
		@SuppressWarnings("unchecked")
		final Map<KIn, VIn> converted = (Map<KIn, VIn>) map;
		return map(converted.entrySet(), function);
	}
	
	
	
	/**
	 * 过滤，只保留过滤函数返回true的值
	 * 
	 * @param collection 待过滤的List&lt;E&gt;
	 * @param function 过滤函数 (E) -> Boolean
	 * @return 过滤后的List&lt;E&gt;
	 * @author ZhangXiaoye
	 * @date 2016年6月17日 下午3:57:47
	 */
	public static <E, T extends Exception> List<E> filter(final Collection<? extends E> collection, final FuncIOE<E, Boolean, T> function) throws T{
		if(collection == null){
			return Collections.emptyList();
		}
		final List<E> ret = new ArrayList<E>(collection.size());
		for(final E obj: collection){
			if(Boolean.TRUE.equals(function.call(obj))){
				ret.add(obj);
			}
		}
		return ret;
	}
	
	/**
	 * 过滤，只保留过滤函数返回true的值
	 * 
	 * @param map 待过滤的Map&lt;K, V&gt;
	 * @param function 过滤函数 (Entry&lt;K, V&gt;) -> Boolean
	 * @return 过滤后的Map&lt;K, V&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午7:40:25
	 */
	public static <K, V, T extends Exception> Map<K, V> filter(final Map<? extends K, ? extends V> map, final FuncIOE<Entry<K, V>, Boolean, T> function) throws T{
		final Map<K, V> ret = new LinkedHashMap<K, V>();
		if(map == null){
			return ret;
		}
		for(final Entry<? extends K, ? extends V> entry: map.entrySet()){
			@SuppressWarnings("unchecked")
			final Entry<K, V> converted = (Entry<K, V>) entry;
			if(Boolean.TRUE.equals(function.call(converted))){
				ret.put(entry.getKey(), entry.getValue());
			}
		}
		return ret;
	}
	
	/**
	 * reduce（对List）
	 * 
	 * @param collection 列表List&lt;E&gt;
	 * @param function 合并函数 (E, E) -> E
	 * @return E合并后结果
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午4:11:49
	 */
	public static <E, T extends Exception> E reduce(final Collection<? extends E> collection, final FuncLROE<E, E, E, T> function) throws T{
		E last = null;
		if(collection != null){
			for(final E obj: collection){
				if(last == null){
					last = obj;
				}else{
					last = function.call(last, obj);
				}
			}
		}
		return last;
	}
	
	/**
	 * reduce（对Map）
	 * 
	 * @param map 待合并的Map&lt;K, List&lt;V&gt;&gt;
	 * @param function 合并函数 (V, V) -> V
	 * @return 合并后的Map&lt;K, V&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午4:16:37
	 */
	public static <K, V, T extends Exception> Map<K, V> reduce(final Map<? extends K, ? extends Collection<? extends V>> map, final FuncLROE<V, V, V, T> function) throws T{
		if(map == null){
			return Collections.emptyMap();
		}
		final Map<K, V> ret = new LinkedHashMap<K, V>();
		for(final Entry<? extends K, ? extends Collection<? extends V>> entry: map.entrySet()){
			final V value = reduce(entry.getValue(), function);
			if(value != null){
				ret.put(entry.getKey(), value);
			}
		}
		return ret;
	}
	
	/**
	 * 左序合并（List）<br/>
	 * 右序请先对列表调用{@link Collections#reverse(List)}
	 * 
	 * @param collection 待合并的List&lt;R&gt;
	 * @param initValue L初始值
	 * @param function 合并函数（L, R）-> L
	 * @return 合并后的List&lt;L&gt;
	 * @author ZhangXiaoye
	 * @date 2016年6月17日 下午3:59:06
	 */
	public static <L, R, T extends Exception> L foldLeft(final Collection<? extends R> collection, final L initValue, final FuncLROE<L, R, L, T> function) throws T{
		L last = initValue;
		if(collection != null){
			for(final R obj: collection){
				last = function.call(last, obj);
			}
		}
		return last;
	}
	
	/**
	 * 左序合并（Map）
	 * 
	 * @param map 待合并的Map&lt;K, List&lt;R&gt;&gt;
	 * @param initValue L初始值
	 * @param function 合并函数（L, R）-> L
	 * @return 合并后的Map&lt;K, L&gt;
	 * @author ZhangXiaoye
	 * @date 2016年9月25日 下午4:19:59
	 */
	public static <K, L, R, T extends Exception> Map<K, L> foldLeft(final Map<? extends K, ? extends Collection<? extends R>> map, final L initValue, final FuncLROE<L, R, L, T> function) throws T{
		if(map == null){
			return Collections.emptyMap();
		}
		final Map<K, L> ret = new LinkedHashMap<K, L>();
		for(final Entry<? extends K, ? extends Collection<? extends R>> entry: map.entrySet()){
			final L value = foldLeft(entry.getValue(), initValue, function);
			if(value != null){
				ret.put(entry.getKey(), value);
			}
		}
		return ret;
	}
	
	/**
	 * 分区（过滤的高级形式），函数的返回值作为key对collection分区。
	 * 
	 * @param collection 待分区的List&lt;V&gt;
	 * @param function 分区函数 (V) -> P，返回null会丢弃
	 * @return 分区后的Map&lt;P, List&lt;V&gt;&gt;，其key由函数决定
	 * @author ZhangXiaoye
	 * @date 2016年6月21日 下午4:42:15
	 */
	public static <P, V, T extends Exception> Map<P, List<V>> partation(final Collection<? extends V> collection, final FuncIOE<V, P, T> function) throws T{
		if(collection == null){
			return Collections.emptyMap();
		}
		final Map<P, List<V>> map = new LinkedHashMap<P, List<V>>();
		for(final V obj: collection){
			final P part = function.call(obj);
			if(part != null){
				List<V> list = map.get(part);
				if(list == null){
					list = new ArrayList<V>();
					map.put(part, list);
				}
				list.add(obj);
			}
		}
		return map;
	}
	
	/**
	 * 与{@link #partation(Collection, FuncIOE)}方法完全相同，请以其替代本方法。<br/>
	 * 分区（过滤的高级形式），函数的返回值作为key对collection分区。
	 * 
	 * @deprecated 将在未来版本移除，请以{@link #partation(Collection, FuncIOE)}方法替代
	 * @param collection 待分区的List&lt;V&gt;
	 * @param function 分区函数 (V) -> P，返回null会丢弃
	 * @return 分区后的Map&lt;P, List&lt;V&gt;&gt;，其key由函数决定
	 * @author ZhangXiaoye
	 * @date 2016年9月29日 下午1:18:31
	 */
	@Deprecated
	public static <P, V, T extends Exception> Map<P, List<V>> partationByLink(final Collection<? extends V> collection, final FuncIOE<V, P, T> function) throws T{
		return partation(collection, function);
	}
	
	/**
	 * join（类似SQL的join）<br/>
	 * 注意：按照hashCode和equals方法匹配左边和右边的列表。<br/>
	 * 实现：在cLeft上遍历，在cRight上建索引。
	 * 
	 * @param cLeft 左边列表List&lt;L&gt;
	 * @param fLeft join左边L的属性名
	 * @param cRight 右边列表List&lt;R&gt;
	 * @param fRight join右边R的属性名
	 * @param function join函数（L，R）-> O
	 * @return join后列表List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年6月17日 下午3:55:14
	 */
	public static <L, R, O, T extends Exception> List<O> join(final Collection<? extends L> cLeft, final String fLeft, final Collection<? extends R> cRight, final String fRight, final FuncLROE<L, R, O, T> function) throws T{
		return join(cLeft, fLeft, cRight, fRight, false, function);
	}
	
	/**
	 * leftJoin（类似SQL的left join）<br/>
	 * 注意：leftJoin可能出现join函数右边R值为null的情况。<br/>
	 * 注意：按照hashCode和equals方法匹配左边和右边的列表。<br/>
	 * 实现：在cLeft上遍历，在cRight上建索引。
	 * 
	 * @param cLeft 左边列表List&lt;L&gt;
	 * @param fLeft join左边L的属性名
	 * @param cRight 右边列表List&lt;R&gt;
	 * @param fRight join右边R的属性名
	 * @param function join函数（L，R）-> O
	 * @return join后列表List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年6月17日 下午3:55:14
	 */
	public static <L, R, O, T extends Exception> List<O> leftJoin(final Collection<? extends L> cLeft, final String fLeft, final Collection<? extends R> cRight, final String fRight, final FuncLROE<L, R, O, T> function) throws T{
		return join(cLeft, fLeft, cRight, fRight, true, function);
	}
	
	/**
	 * join（类似SQL的join）<br/>
	 * 注意：按照hashCode和equals方法匹配左边和右边的列表。<br/>
	 * 实现：在cLeft上遍历，在cRight上建索引。
	 * 
	 * @param cLeft 左边列表List&lt;L&gt;
	 * @param fLeft join左边L的属性名
	 * @param cRight 右边列表List&lt;R&gt;
	 * @param fRight join右边R的属性名
	 * @param leftJoin 是否允许leftJoin
	 * @param function join函数（L，R）-> O
	 * @return join后列表List&lt;O&gt;
	 * @author ZhangXiaoye
	 * @date 2016年6月17日 下午3:55:14
	 */
	private static <L, R, O, T extends Exception> List<O> join(final Collection<? extends L> cLeft, final String fLeft, final Collection<? extends R> cRight, final String fRight, final boolean leftJoin, final FuncLROE<L, R, O, T> function) throws T{
		if(StringUtils.isBlank(fLeft)){
			throw new IllegalArgumentException("c1Field is blank.");
		}
		if(StringUtils.isBlank(fRight)){
			throw new IllegalArgumentException("c2Field is blank.");
		}
		if(cLeft == null){
			return Collections.emptyList();
		}
		final String c2Method = "get" + fRight.substring(0, 1).toUpperCase() + fRight.substring(1);
		final String c1Method = "get" + fLeft.substring(0, 1).toUpperCase() + fLeft.substring(1);
		// 建c2索引
		final Map<Object, List<R>> c2Index = new HashMap<Object, List<R>>();
		if(cRight != null){
			MethodAccess ma2 = null;
			int mi2 = 0;
			for(final R obj2: cRight){
				if(obj2 == null){
					continue;
				}
				if(ma2 == null){
					ma2 = MethodAccess.get(obj2.getClass());
					mi2 = ma2.getIndex(c2Method);
				}
				final Object v2 = ma2.invoke(obj2, mi2);
				if(v2 != null){
					List<R> idx2 = c2Index.get(v2);
					if(idx2 == null){
						idx2 = new ArrayList<R>();
						c2Index.put(v2, idx2);
					}
					idx2.add(obj2);
				}
			}
		}
		// 遍历c1
		final List<O> ret = new ArrayList<O>();
		MethodAccess ma1 = null;
		int mi1 = 0;
		for(final L obj1: cLeft){
			if(obj1 == null){
				continue;
			}
			if(ma1 == null){
				ma1 = MethodAccess.get(obj1.getClass());
				mi1 = ma1.getIndex(c1Method);
			}
			final Object v1 = ma1.invoke(obj1, mi1);
			if(v1 != null){
				List<R> idx2 = c2Index.get(v1);
				if(idx2 != null && idx2.size() > 0){
					for(R obj2: idx2){
						ret.add(function.call(obj1, obj2));
					}
				}else if(leftJoin){
					ret.add(function.call(obj1, null));
				}
			}else if(leftJoin){
				ret.add(function.call(obj1, null));
			}
		}
		return ret;
	}
	
}
